/*
 * Copyright © OpenAtom Foundation.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package io.iec.edp.caf.databaseobject;

import io.iec.edp.caf.databaseobject.api.entity.DbType;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

import java.util.Locale;

/**
 * @author liu_wei
 */
public class DboPhysicalNamingStrategy implements PhysicalNamingStrategy {

    private DbType dbType;

    public void setDbType(DbType dbType){
        this.dbType = dbType;
    }

    @Override
    public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return apply(name, true, jdbcEnvironment);
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return apply(name, true, jdbcEnvironment);
    }

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        //表名需要区分大小写，否则会出现表结构更新报错的问题
        return apply(name, false, jdbcEnvironment);
    }

    @Override
    public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return apply(name, true, jdbcEnvironment);
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return apply(name, true, jdbcEnvironment);
    }

    /**
     * 获取处理后的标识符
     *
     * @param name            原标识符
     * @param ignoreCase      是否忽略大小写
     * @param jdbcEnvironment jdbc运行环境
     * @return 处理后的标识符
     */
    private Identifier apply(Identifier name, Boolean ignoreCase, JdbcEnvironment jdbcEnvironment) {
        if (name == null) {
            return null;
        }
//        StringBuilder builder = new StringBuilder(name.getText().replace('.', '_'));
//        for (int i = 1; i < builder.length() - 1; i++) {
//            if (isUnderscoreRequired(builder.charAt(i - 1), builder.charAt(i), builder.charAt(i + 1))) {
//                builder.insert(i++, '_');
//            }
//        }
        return getIdentifier(name.getText(), name.isQuoted(), ignoreCase, jdbcEnvironment);
    }

    /**
     * Get an identifier for the specified details. By default this method will return an
     * identifier with the name adapted based on the result of  {@link #lowerOrUpperCase(JdbcEnvironment)}
     *
     * @param name            the name of the identifier
     * @param quoted          if the identifier is quoted
     * @param ignoreCase      是否忽略大小写
     * @param jdbcEnvironment the JDBC environment
     * @return an identifier instance
     */
    private Identifier getIdentifier(String name, boolean quoted, Boolean ignoreCase, JdbcEnvironment jdbcEnvironment) {
        LetterCase lc = ignoreCase ? LetterCase.insensitive : lowerOrUpperCase(jdbcEnvironment);
        switch (lc) {
            case lowerCase:
                name = name.toLowerCase(Locale.ROOT);
                break;
            case upperCase:
                name = name.toUpperCase(Locale.ROOT);
                break;
            case insensitive:
            default:
                break;
        }
        return new Identifier(name, quoted);
    }

    /**
     * 需要根据数据库类型识别转大写、小写或大小写不敏感
     *
     * @param jdbcEnvironment the JDBC environment which can be used to determine case
     * @return true if the database is case insensitive sensitivity
     */
    private LetterCase lowerOrUpperCase(JdbcEnvironment jdbcEnvironment) {
        //https://blog.csdn.net/shipeng22022/article/details/40950591
        // 根据跟踪的实际执行的sql，其中不包含引号，不需要进行大小写转换，此处暂时屏蔽
        Dialect dialect = jdbcEnvironment.getDialect();
        if (dialect == null) {
            return LetterCase.insensitive;
        }

        String[] arr = dialect.toString().split("\\.");
        String name = arr[arr.length - 1].toLowerCase(Locale.ROOT);
        if(DbType.OpenGauss.equals(dbType)){
            return LetterCase.lowerCase;
        }
        if (name.contains("postgresql") || name.contains("hgdbdialect") || name.contains("mysql") || name.contains("gbase")) {
            return LetterCase.lowerCase;
        } else if (name.contains("oracle") || name.contains("dm") || name.contains("oscar") || name.contains("db2")) {
            return LetterCase.upperCase;
        }
        return LetterCase.insensitive;
    }

    /**
     * 判断当前字符是否为单词首字母
     *
     * @param before  前一个字符
     * @param current 当前字符
     * @param after   后一字符
     * @return 如果当前字符为单词的首字母，则返回True
     */
    private boolean isUnderscoreRequired(char before, char current, char after) {
        return Character.isLowerCase(before) && Character.isUpperCase(current) && Character.isLowerCase(after);
    }
}
